﻿/***************************************************************
 * Name:      MinesweeperMain.cpp
 * Purpose:   Code for Application Frame
 * Author:    PhoenixWright (cbx945+dev@gmail.com)
 * Created:   2011-04-30
 * Copyright: PhoenixWright (http://www.itcbx.com)
 * License:
 **************************************************************/

#ifdef WX_PRECOMP
#include "wx_pch.h"
#endif

#ifdef __BORLANDC__
#pragma hdrstop
#endif //__BORLANDC__

#include <stdlib.h>
#include <time.h>
#include <string.h>

#include <wx/colour.h>

#include "MinesweeperMain.h"

#include "PosFixed.h"
#include "IdDefine.h"

//helper functions
enum wxbuildinfoformat {
    short_f, long_f };

wxString wxbuildinfo(wxbuildinfoformat format)
{
    wxString wxbuild(wxVERSION_STRING);

    if (format == long_f )
    {
#if defined(__WXMSW__)
        wxbuild << _T("-Windows");
#elif defined(__WXMAC__)
        wxbuild << _T("-Mac");
#elif defined(__UNIX__)
        wxbuild << _T("-Linux");
#endif

#if wxUSE_UNICODE
        wxbuild << _T("-Unicode build");
#else
        wxbuild << _T("-ANSI build");
#endif // wxUSE_UNICODE
    }

    return wxbuild;
}

BEGIN_EVENT_TABLE(MinesweeperFrame, wxFrame)
    EVT_CLOSE(MinesweeperFrame::OnClose)
    EVT_MENU(wxID_EXIT, MinesweeperFrame::OnQuit)
    EVT_MENU(wxID_ABOUT, MinesweeperFrame::OnAbout)
    EVT_MENU(wxID_NEW_GAME, MinesweeperFrame::OnNewGame)
    EVT_PAINT(MinesweeperFrame::OnPaint)
    EVT_RIGHT_DOWN(MinesweeperFrame::OnRightDown)
    EVT_LEFT_DOWN(MinesweeperFrame::OnLeftDown)
    EVT_LEFT_UP(MinesweeperFrame::OnLeftUp)
    EVT_MIDDLE_DOWN(MinesweeperFrame::OnMiddleDown)
    EVT_MOTION(MinesweeperFrame::OnMotion)
    EVT_SIZE(MinesweeperFrame::OnSize)
END_EVENT_TABLE()

MinesweeperFrame::MinesweeperFrame(wxFrame *frame, const wxString& title)
    : wxFrame(frame, -1, title)
{
#if wxUSE_MENUS
    wxMenuBar* mbar = new wxMenuBar();
    wxMenu* gameMenu = new wxMenu();
    gameMenu->Append(wxID_NEW_GAME, _("&New Game\tF2"));
    gameMenu->AppendSeparator();
    gameMenu->Append(wxID_STATISTICS, _("&Statistics\tF4"));
    gameMenu->Append(wxID_OPTIONS, _("&Options\tF5"));
    gameMenu->Append(wxID_CHANGE_APPERANCE, _("Change &Apperance\tF7"));
    gameMenu->AppendSeparator();
    gameMenu->Append(wxID_EXIT, _("E&xit"));

    wxMenu* helpMenu = new wxMenu();
    helpMenu->Append(wxID_VIEW_HELP, _("&View Help\tF1"));
    helpMenu->AppendSeparator();
    helpMenu->Append(wxID_ABOUT, _("&About Minesweeper"));

    mbar->Append(gameMenu, _("&Game"));
    mbar->Append(helpMenu, _("&Help"));

    SetMenuBar(mbar);
#endif // wxUSE_MENUS

#if wxUSE_STATUSBAR
    CreateStatusBar(2);
    SetStatusText(_("Author: Phoenix"), 0);
    SetStatusText(_T("http://www.itcbx.com"), 1);
#endif // wxUSE_STATUSBAR

    // 初始鼠标的上一次位置为-1,代表无
    this->mousePrevPosition.x = this->mousePrevPosition.y = -1;
    // 缩放比例默认为1
    this->scaleX = this->scaleY = 1.0;
    // 雷区开始位置
    this->ptGame.x = this->ptGame.y = 50;
    // 雷区的行列数,雷数
    // TODO (phoenixwright#1#): 这里可以改为通过配置文件获取行列数
    mineLogic.rows = 20, mineLogic.cols = 30, mineLogic.mines = 66;
    mineLogic.blocks = mineLogic.rows * mineLogic.cols - mineLogic.mines;
    mineLogic.restart = false;
    mineLogic.GernalMineMap();
    this->InitSources();
    this->SetClientSize(ptGame.x*2+blockWidth*mineLogic.cols, ptGame.y*2+blockHeight*mineLogic.rows);
    defaultClientSize = this->GetClientSize();
    this->SetMinSize(this->GetSize());
}

void MinesweeperFrame::InitSources() {
    wxImage greenImage(_T("greenmap.bmp"), wxBITMAP_TYPE_BMP);
    int x = 0, y = 0;

    mark = greenImage.GetSubImage(wxRect(20*blockWidth, 28*blockHeight, blockWidth, blockHeight));
    question = greenImage.GetSubImage(wxRect(21*blockWidth, 28*blockHeight, blockWidth, blockHeight));
    mark.SetMaskColour(0, 0, 0), question.SetMaskColour(0, 0, 0);
    mark.SetMask(true), question.SetMask(true);
    for(int i = 0; i < 720; ++i) {
        greenUnknown[i] = greenImage.GetSubImage(wxRect(x, y, blockWidth, blockHeight));
        if((i+1) % 25 == 0) {
            x = 0, y += blockHeight;
        } else {
            x += blockWidth;
        }
    }
    x = 25 * blockWidth, y = 0;
    for(int i = 0; i < 720; ++i) {
        greenKnown[i] = greenImage.GetSubImage(wxRect(x, y, blockWidth, blockHeight));
        if((i+1) % 25 == 0) {
            x = 25 * blockWidth, y += blockHeight;
        } else {
            x += blockWidth;
        }
    }

    // TODO (phoenixwright#1#): 这里可根据配置文件读取不同的主题
    known = greenKnown;
    unknown = greenUnknown;

    x = 20 * 50, y = 0;
    for(int i = 1; i <= 8; ++i) {
        number[i] = greenImage.GetSubImage(wxRect(x, y, numWidth, numHeight));
        number[i].SetMaskColour(0, 0, 0);
        number[i].SetMask(true);
        y += numHeight;
    }
}

void MinesweeperFrame::OnLeftDown(wxMouseEvent &event) {
    int x, y, x1, y1;
    this->AdjustPosition(event.GetPosition().x, event.GetPosition().y, x, y, x1, y1);
    if(!IsValid(y, x)) return;
    mousePrevPosition.x = event.GetPosition().x, mousePrevPosition.y = event.GetPosition().y;
    if(event.m_rightDown) {
        //OnMiddleDown(event);
        return;
    }
    if(mineLogic.userStatus[y][x] == NO_ACTION || mineLogic.userStatus[y][x] == QUESTION) {
        wxClientDC dc(this);
        dc.SetUserScale(scaleX, scaleY);
        dc.DrawBitmap(*(known+AdjustKnownPosition(y, x)), x1, y1);
        if(mineLogic.userStatus[y][x] == QUESTION) {
            dc.DrawBitmap(question, x1, y1, true);
        }
    }
}

void MinesweeperFrame::OnLeftUp(wxMouseEvent &event) {
    if(event.m_rightDown) {
        OnMiddleDown(event);
        return;
    }
    if(mousePrevPosition.x == -1 || mousePrevPosition.y == -1) return;
    wxClientDC dc(this);
    dc.SetUserScale(scaleX, scaleY);
    // 上一次的userStatus索引位置
    int preX1, preY1;
    // 上一次界面需要还原的位置
    int preX2, preY2;
    // 此次userStatus索引位置和界面需绘制的位置
    int x1, y1, x2, y2;
    AdjustPosition(mousePrevPosition.x, mousePrevPosition.y, preX1, preY1, preX2, preY2);
    AdjustPosition(event.GetPosition().x, event.GetPosition().y, x1, y1, x2, y2);

    // 如果索引位置不同，才需要还原
    if(preX1 != x1 || preY1 != y1) {
        if(mineLogic.userStatus[preY1][preX1] == NO_ACTION) {
            dc.DrawBitmap(*(unknown+AdjustUnknownPosition(preY1, preX1)), preX2, preY2);
        } else if(mineLogic.userStatus[preY1][preX1] == QUESTION) {
            dc.DrawBitmap(*(unknown+AdjustUnknownPosition(preY1, preX1)), preX2, preY2);
            dc.DrawBitmap(question, preX2, preY2, true);
        }
    }
    mousePrevPosition.x = mousePrevPosition.y = -1;
    if(mineLogic.Click(y1, x1)) {
        RefreshMineMap(dc);
    }
    if(mineLogic.status == SUCESS) {
        wxMessageBox(_T("Success!"));
        NewGame();
    }
    if(mineLogic.status == FAILED) {
        wxMessageBox(_T("FAILED"));
        NewGame();
    }
}

void MinesweeperFrame::OnMotion(wxMouseEvent &event) {
    if(event.Dragging() && (event.m_leftDown)) {
        if(event.m_rightDown) {
            return;
        }
        wxClientDC dc(this);
        dc.SetUserScale(scaleX, scaleY);
        // 上一次的userStatus索引位置
        int preX1, preY1;
        // 上一次界面需要还原的位置
        int preX2, preY2;
        // 此次userStatus索引位置和界面需绘制的位置
        int x1, y1, x2, y2;
        AdjustPosition(mousePrevPosition.x, mousePrevPosition.y, preX1, preY1, preX2, preY2);
        AdjustPosition(event.GetPosition().x, event.GetPosition().y, x1, y1, x2, y2);
        if(IsValid(preY1, preX1)) {
            // 如果索引位置不同，才需要还原
            if(preX1 != x1 || preY1 != y1) {
                if(mineLogic.userStatus[preY1][preX1] == NO_ACTION) {
                    dc.DrawBitmap(*(unknown+AdjustUnknownPosition(preY1, preX1)), preX2, preY2);
                } else if(mineLogic.userStatus[preY1][preX1] == QUESTION) {
                    dc.DrawBitmap(*(unknown+AdjustUnknownPosition(preY1, preX1)), preX2, preY2);
                    dc.DrawBitmap(question, preX2, preY2, true);
                }
            }
        }
        if(!IsValid(y1, x1)) {
            mousePrevPosition.x = mousePrevPosition.y = -1;
            return;
        }
        if(mineLogic.userStatus[y1][x1] == NO_ACTION || mineLogic.userStatus[y1][x1] == QUESTION) {
            dc.DrawBitmap(*(known+AdjustKnownPosition(y1, x1)), x2, y2);
            if(mineLogic.userStatus[y1][x1] == QUESTION) {
                dc.DrawBitmap(question, x2, y2, true);
            }
        }

        mousePrevPosition.x = event.GetPosition().x;
        mousePrevPosition.y = event.GetPosition().y;
    }
}

void MinesweeperFrame::OnRightDown(wxMouseEvent &event) {
    int x1, y1, x2, y2;
    AdjustPosition(event.GetPosition().x, event.GetPosition().y, x1, y1, x2, y2);
    if(!IsValid(y1, x1)) return;
    wxClientDC dc(this);
    dc.SetUserScale(scaleX, scaleY);
    if(event.m_leftDown) {
        // TODO (phoenixwright#1#): 处理左右建同时响应事件
        return;
    }
    if(mineLogic.userStatus[y1][x1] == NO_ACTION) {
        dc.DrawBitmap(mark, x2, y2, true);
        mineLogic.userStatus[y1][x1] = MARK;
    } else if(mineLogic.userStatus[y1][x1] != DIG) {
        dc.DrawBitmap(*(unknown+AdjustUnknownPosition(y1, x1)), x2, y2);
        if(mineLogic.userStatus[y1][x1] == MARK) {
            dc.DrawBitmap(question, x2, y2, true);
            mineLogic.userStatus[y1][x1] = QUESTION;
        } else {
            mineLogic.userStatus[y1][x1] = NO_ACTION;
        }
    }
}

void MinesweeperFrame::OnMiddleDown(wxMouseEvent &event) {
    int x1, y1, x2, y2;
    AdjustPosition(event.GetPosition().x, event.GetPosition().y, x1, y1, x2, y2);
    if(mineLogic.MiddleClick(y1, x1)) {
        wxClientDC dc(this);
        dc.SetUserScale(scaleX, scaleY);
        RefreshMineMap(dc);
    }
    if(mineLogic.status == SUCESS) {
        wxMessageBox(_T("Success!"));
        NewGame();
    } else if(mineLogic.status == FAILED) {
        wxMessageBox(_T("Failed!"));
        NewGame();
    }
}

void MinesweeperFrame::RefreshMineMap(wxDC &dc) {
    int row, col;
    while(mineLogic.ArrayWalk(row, col)) {
        int x = col*blockWidth + ptGame.x;
        int y = row*blockHeight + ptGame.y;
        if(mineLogic.userStatus[row][col] == DIG) {
            dc.DrawBitmap(*(known+AdjustKnownPosition(row, col)), x, y);
            if(mineLogic.mineStatus[row][col] > 0) {
                dc.DrawBitmap(number[mineLogic.mineStatus[row][col]], x+numPosXFixed, y+numPosYFixed, true);
            }
        } else {
            dc.DrawBitmap(*(unknown+AdjustUnknownPosition(row, col)), x, y);
            if(mineLogic.userStatus[row][col] == MARK) {
                dc.DrawBitmap(mark, x, y, true);
            } else if(mineLogic.userStatus[row][col] == QUESTION) {
                dc.DrawBitmap(question, x, y, true);
            }
        }
    }
}

/** \brief 转换坐标
 *
 * \param x 鼠标横坐标
 * \param y 鼠标纵坐标
 * \param x1 引用参数 userStatus中的横坐标索引
 * \param y1 引用参数 userStatus中的纵坐标索引
 * \param x2 引用参数 将画在界面上的图片的横坐标索引
 * \param y2 引用参数 将画在界面上的图片的纵坐标索引
 * 将x,y对应的鼠标坐标转换为userStatus中的相应索引和将画在界面上的图片的位置
 */

void MinesweeperFrame::AdjustPosition(int x, int y, int &x1, int &y1, int &x2, int &y2) {
    double tx = x / scaleX, ty = y / scaleY;
    if(tx-ptGame.x < 0 || ty-ptGame.y < 0) {
        x1 = y1 = x2 = y2 = -1;
        return;
    }
    x1 = 1.0 * (tx-ptGame.x) / blockWidth, y1 = 1.0 * (ty-ptGame.y) / blockHeight;
    x2 = x1*blockWidth + ptGame.x;
    y2 = y1*blockHeight + ptGame.y;
}

int MinesweeperFrame::AdjustKnownPosition(int row, int col) {
    return row * 30 + col;
}

int MinesweeperFrame::AdjustUnknownPosition(int row, int col) {
    return row * 30 + (30-mineLogic.cols+col);
}

bool MinesweeperFrame::IsValid(int row, int col) {
    return row >= 0 && row < mineLogic.rows && col >= 0 && col < mineLogic.cols;
}

void MinesweeperFrame::OnPaint(wxPaintEvent &event) {
    wxPaintDC dc(this);
    dc.SetUserScale(scaleX, scaleY);
    int x = ptGame.x, y = ptGame.y;
    for(int i = 0; i < mineLogic.rows; ++i) {
        for(int j = 0; j < mineLogic.cols; ++j) {
            if(mineLogic.userStatus[i][j] == DIG) {
                dc.DrawBitmap(*(known+AdjustKnownPosition(i, j)), x, y);
                if(mineLogic.mineStatus[i][j] > 0) {
                    dc.DrawBitmap(number[mineLogic.mineStatus[i][j]], x+6, y+3, true);
                }
            } else {
                dc.DrawBitmap(*(unknown+AdjustUnknownPosition(i, j)), x, y);
                if(mineLogic.userStatus[i][j] == MARK) {
                    dc.DrawBitmap(mark, x, y, true);
                } else if(mineLogic.userStatus[i][j] == QUESTION) {
                    dc.DrawBitmap(question, x, y, true);
                }
            }
            x += blockWidth;
        }
        x = ptGame.x;
        y += blockHeight;
    }
}

void MinesweeperFrame::OnSize(wxSizeEvent &event) {
    wxSize size = this->GetClientSize();
    scaleX = 1.0 * size.x / defaultClientSize.x;
    scaleY = 1.0 * size.y / defaultClientSize.y;
    Refresh();
}

void MinesweeperFrame::NewGame(bool _restart) {
    // TODO (phoenixwright#1#): 这里需要从配置中读取地图参数初始化mingLogic的一些值
    mineLogic.NewGame(_restart);
    // 初始鼠标的上一次位置为-1,代表无
    mousePrevPosition.x = mousePrevPosition.y = -1;
    this->SetClientSize(ptGameX*2+blockWidth*mineLogic.cols, ptGameY*2+blockHeight*mineLogic.rows);
    defaultClientSize = this->GetClientSize();
    this->SetMinSize(this->GetSize());
    Refresh();
}

void MinesweeperFrame::OnClose(wxCloseEvent &event)
{
    Destroy();
}

void MinesweeperFrame::OnQuit(wxCommandEvent &event)
{
    Destroy();
}

void MinesweeperFrame::OnAbout(wxCommandEvent &event)
{
    wxString msg = wxbuildinfo(long_f);
    wxMessageBox(msg, _("Welcome to..."));
}

void MinesweeperFrame::OnNewGame(wxCommandEvent &event)
{
    NewGame();
}
